#region Imported Namespaces


using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;


#endregion


namespace WtfManager.Framework
{
  internal class DependenciesCollection: Collection<Type>
  {
    #region Fields


    private readonly List<Service> _monitoredServices = new List<Service>();
    private bool _isDependencyServicesReady;


    #endregion


    #region Constructors/Finalizers


    public DependenciesCollection()
    {
      INotifyCollectionChanged servicesCollection = WtfFramework.Instance.Services;
      servicesCollection.CollectionChanged += ServicesCollectionChanged;
    }


    #endregion


    #region Properties/Indexers


    public bool IsDependencyServicesReady
    {
      get
      {
        return _isDependencyServicesReady;
      }
      private set
      {
        if (_isDependencyServicesReady == value)
        {
          return;
        }

        _isDependencyServicesReady = value;

        if (IsDependencyServicesReadyChanged != null)
        {
          IsDependencyServicesReadyChanged(this, EventArgs.Empty);
        }
      }
    }


    #endregion


    #region Events


    public event EventHandler IsDependencyServicesReadyChanged;


    #endregion


    #region Methods


    private void ServicesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      var list = (IList)sender;

      switch (e.Action)
      {
        case NotifyCollectionChangedAction.Add:
          ProcessAddedServices(e.NewItems);
          break;

        case NotifyCollectionChangedAction.Remove:
          ProcessRemovedServices(e.OldItems);
          break;

        case NotifyCollectionChangedAction.Replace:
          ProcessRemovedServices(e.OldItems);
          ProcessAddedServices(e.NewItems);
          break;

        case NotifyCollectionChangedAction.Reset:
          ProcessAddedServices(list);
          ProcessAddedServices(list);
          break;
      }
    }


    private void ProcessRemovedServices(IList oldItems)
    {
      foreach (Service service in oldItems)
      {
        if (!Contains(service.GetType()))
        {
          continue;
        }

        RemoveMonitoredService(service);
      }

      CheckServices();
    }


    private void ProcessAddedServices(IList newItems)
    {
      foreach (Service service in newItems)
      {
        if (!Contains(service.GetType()))
        {
          continue;
        }

        AddMonitoredService(service);
      }

      CheckServices();
    }


    private void AddMonitoredService(Service service)
    {
      if (service != null)
      {
        service.StateChanged += MonitoredServiceStateChanged;
        _monitoredServices.Add(service);
      }
    }


    private void RemoveMonitoredService(Service service)
    {
      if (service != null)
      {
        service.StateChanged -= MonitoredServiceStateChanged;
        _monitoredServices.Remove(service);
      }
    }


    private void MonitoredServiceStateChanged(object sender, EventArgs e)
    {
      CheckServices();
    }


    private void CheckServices()
    {
      if (_monitoredServices.Count < Count)
      {
        IsDependencyServicesReady = false;
        return;
      }

      foreach (Service service in _monitoredServices)
      {
        if (!service.IsInitialized)
        {
          IsDependencyServicesReady = false;
          return;
        }
      }

      IsDependencyServicesReady = true;
    }


    protected override void ClearItems()
    {
      var services = new List<Service>(_monitoredServices);

      foreach (Service service in services)
      {
        RemoveMonitoredService(service);
      }

      base.ClearItems();

      CheckServices();
    }


    protected override void InsertItem(int index, Type item)
    {
      ValidateItem(item);

      Service service = WtfFramework.Instance.Services[item];

      AddMonitoredService(service);

      base.InsertItem(index, item);

      CheckServices();
    }


    protected override void RemoveItem(int index)
    {
      Service service = GetService(index);

      RemoveMonitoredService(service);

      base.RemoveItem(index);

      CheckServices();
    }


    protected override void SetItem(int index, Type item)
    {
      ValidateItem(item);

      Service removedService = GetService(index);
      RemoveMonitoredService(removedService);

      Service service = WtfFramework.Instance.Services[item];
      AddMonitoredService(service);

      base.SetItem(index, item);

      CheckServices();
    }


    private static void ValidateItem(Type type)
    {
      if (type == null || !type.IsSubclassOf(typeof(Service)))
      {
        throw new InvalidOperationException("A specified Type is not a subclass of Service type.");
      }
    }


    private Service GetService(int index)
    {
      Type item = this[index];
      return WtfFramework.Instance.Services[item];
    }


    #endregion
  }
}